using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Design;

namespace InfFront.InfoSources.Base.Design
{
    public partial class InfoSourceContentsDesignerControl : BaseDesignerControl
    {
        List<IInfoSourceDesignTimeAction> additionalActions = null;

        public InfoSourceContentsDesignerControl()
        {
            InitializeComponent();
        }

        private InfoSource InfoSource
        {
            get { return (InfoSource)Component; }
        }

        protected override void Rebuild()
        {
            base.Rebuild();
            if (additionalActions != null)
            {
                additionalActions.Clear();
                additionalActions = null;
            }


            if (InfoSource != null)
            {
                ctrlFieldsDesigner.RebuildWith(
                    new OwnedListHanler<InfoField>(this, InfoSource.Fields, 
                                                   InfoSourceDesignTimeActionTarget.Fields)
                    );
                ctrlFieldGroupsDesigner.RebuildWith(
                    new OwnedListHanler<InfoFieldGroup>(this, InfoSource.FieldGroups, 
                                                        InfoSourceDesignTimeActionTarget.FieldGroups)
                    );
                ctrlCommandsDesigner.RebuildWith(
                    new OwnedListHanler<InfoCommand>(this, InfoSource.Commands, 
                                                     InfoSourceDesignTimeActionTarget.Commands)
                    );
            }
            else
            {
                ctrlFieldsDesigner.RebuildWith(null);
                ctrlFieldGroupsDesigner.RebuildWith(null);
                ctrlCommandsDesigner.RebuildWith(null);
            }
        }

        /// <summary>
        /// generic InfoSourceOwnedListDesignerControl.IHandler implementation
        /// </summary>
        /// <typeparam name="T">Item type</typeparam>
        private class OwnedListHanler<T> : InfoSourceOwnedListDesignerControl.IHandler
            where T : class, IInfoSourceOwnedObject
        {
            #region Private Variables
            InfoSourceContentsDesignerControl owner;
            InfoSourceOwnedListOf<T> list;
            InfoSourceDesignTimeActionTarget target;
            #endregion Private Variables

            public OwnedListHanler(InfoSourceContentsDesignerControl owner, InfoSourceOwnedListOf<T> list,
                                   InfoSourceDesignTimeActionTarget target)
            {
                this.owner = owner;
                this.list = list;
                this.target = target;
            }

            public IEnumerable<IInfoSourceOwnedObject> GetItemsToShow()
            {
                var actualComponents = new System.Collections.ArrayList(owner.Designer.Component.Site.Container.Components);
                foreach (var item in list)
                {
                    if (!actualComponents.Contains(item)) // filter out any kind of "derived" collection items
                        continue;
                    yield return item;
                }
            }

            public bool HasAdditionalActions()
            {
                return owner.HastActionsByTarget(target);
            }

            public IEnumerable<InfoSourceOwnedListDesignerControl.IAction> GetAdditionalActions()
            {
                foreach (var action in owner.GetActionsByTarget(target))
                {
                    yield return new AdditionalAction() { Origin = action };
                }
            }

            private class AdditionalAction: InfoSourceOwnedListDesignerControl.IAction
            {
                public IInfoSourceDesignTimeAction Origin { get; set; }

                public string Caption
                {
                    get { return Origin.Caption; }
                }
            }
            
            public void ExecuteAction(InfoSourceOwnedListDesignerControl.IAction action)
            {
                var aact = (AdditionalAction)action;
                aact.Origin.Execute();
            }

            public IEnumerable<Type> GetTypesForCreation()
            {
                var tdService = owner.GetService<ITypeDiscoveryService>();
                foreach (Type type in tdService.GetTypes(typeof(T), false))
                {
                    if (type.IsAbstract || type.IsGenericType)
                        continue;
                    if (type.IsNotPublic && !type.Assembly.Equals(owner.DesignerHost.RootComponent.GetType().Assembly))
                        continue;
                    yield return type;
                }
            }

            public void CreateNewItem(Type type)
            {
                owner.DesignerHost.CreateComponent(type);
            }

            public void DestroyItem(IInfoSourceOwnedObject obj)
            {
                owner.DesignerHost.DestroyComponent(obj);
            }

            public bool ActOnObjectChanged(IInfoSourceOwnedObject obj)
            {
                return (obj as T) != null;
            }

            public bool ActOnObjectRemoved(IInfoSourceOwnedObject obj)
            {
                var asT = obj as T;
                if (asT == null)
                    return false;
                return list.Remove(asT);
            }

            public bool ActOnObjectAdded(IInfoSourceOwnedObject obj)
            {
                var asT = obj as T;
                if (asT == null)
                    return false;

                asT.Order = list.Count;
                list.Add(asT);
                return true;
            }

            public void RecalcOrders()
            {
                int i = 0;
                foreach (var itm in list)
                {
                    var oldOrder = itm.Order;
                    if (oldOrder != i)
                    {
                        var props = TypeDescriptor.GetProperties(itm);
                        var prop = props["Order"];
                        owner.ChangeService.OnComponentChanging(itm, prop);
                        itm.Order = i;
                        owner.ChangeService.OnComponentChanged(itm, prop, oldOrder, i);
                    }
                    ++i;
                }
            }

            public void SelectComponent(object component)
            {
                if (component == null)
                    component = owner.Component;
                owner.SelectComponent(component);
            }

            public void MoveItemUp(IInfoSourceOwnedObject obj)
            {
                var asT = (T) obj;
                int index = list.IndexOf(asT);
                list.Remove(asT);
                list.Insert(index - 1, asT);

                RecalcOrders();
            }

            public void MoveItemDown(IInfoSourceOwnedObject obj)
            {
                var asT = (T)obj;
                int index = list.IndexOf(asT);
                list.Remove(asT);
                list.Insert(index + 1, asT);

                RecalcOrders();
            }
        }

        protected bool HastActionsByTarget(InfoSourceDesignTimeActionTarget target)
        {
			return GetActionsByTarget(target).Any();
        }

        protected IEnumerable<IInfoSourceDesignTimeAction> GetActionsByTarget(InfoSourceDesignTimeActionTarget target)
        {
            if (additionalActions == null)
            {
                additionalActions = new List<IInfoSourceDesignTimeAction>();
                var attrs = InfoSource.GetType().GetCustomAttributes(typeof(InfoSourceDesignTimeExtensionAttribute), true);
                foreach (InfoSourceDesignTimeExtensionAttribute attr in attrs)
                {
                    var ext = attr.CreateExtension();
                    ext.Designer = Designer;
                    foreach (var action in ext.Actions)
                        additionalActions.Add(action);
                }
            }
            foreach (var action in additionalActions)
            {
                if (action.Target == target)
                    yield return action;
            }
        }

        protected override void OnAComponentChanged(object sender, ComponentChangedEventArgs e)
        {
            base.OnAComponentChanged(sender, e);

            var asOwnObj = e.Component as IInfoSourceOwnedObject;
            if (asOwnObj != null)
            {
                bool acted = ctrlFieldsDesigner.ActOnObjectChanged(asOwnObj) 
                          || ctrlFieldGroupsDesigner.ActOnObjectChanged(asOwnObj)
                          || ctrlCommandsDesigner.ActOnObjectChanged(asOwnObj);
            }
        }

        protected override void OnAComponentRename(object sender, ComponentRenameEventArgs e)
        {
            base.OnAComponentRename(sender, e);
        }

        protected override void OnComponentRemoved(object sender, ComponentEventArgs e)
        {
            var asOwnObj = e.Component as IInfoSourceOwnedObject;
            if (asOwnObj != null)
            {
                bool acted = ctrlFieldsDesigner.ActOnObjectRemoved(asOwnObj)
                          || ctrlFieldGroupsDesigner.ActOnObjectRemoved(asOwnObj)
                          || ctrlCommandsDesigner.ActOnObjectRemoved(asOwnObj);
            }
        }

        protected override void OnComponentAdded(object sender, ComponentEventArgs e)
        {
            var asOwnObj = e.Component as IInfoSourceOwnedObject;
            if (asOwnObj != null)
            {
                bool acted = ctrlFieldsDesigner.ActOnObjectAdded(asOwnObj)
                          || ctrlFieldGroupsDesigner.ActOnObjectAdded(asOwnObj)
                          || ctrlCommandsDesigner.ActOnObjectAdded(asOwnObj);
            }
        }
    }
}
